/********************************************************************************
 * Project Name		[JavaEE_Components]
 * File Name     	[MailUtils.java]
 * Creation Date 	[2015-01-01]
 * 
 * Copyright© ge.y.yang@gmail.com All Rights Reserved
 * 
 * Work hard, play harder, think big and keep fit
 ********************************************************************************/
package javamail.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Flags;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

/**
 * Email工具类
 * 
 * @author 不落的太阳(Sean Yang aka ShortPeace)
 * @version 1.0
 * @since jdk 1.8
 * 
 */
public class MailUtils {

	/**
	 * 根据指定的文件名生成附件
	 * 
	 * @param fileName
	 *            文件名
	 * @return
	 * @throws Exception
	 */
	public static MimeBodyPart createMailAttachment(File fileName) throws Exception {
		MimeBodyPart attachment = new MimeBodyPart();
		DataSource dataSource = new FileDataSource(fileName);
		DataHandler dataHandler = new DataHandler(dataSource);
		attachment.setDataHandler(dataHandler);
		attachment.setFileName(MimeUtility.encodeText(dataSource.getName()));
		return attachment;
	}

	/**
	 * 根据指定路径的文件名生成附件
	 * 
	 * @param filePath
	 *            附件所在的文件路径
	 * @return
	 * @throws Exception
	 */
	public static MimeBodyPart createMailAttachment(String filePath) throws Exception {
		File file = new File(filePath);
		return createMailAttachment(file);
	}

	/**
	 * 根据指定的邮件正文文本和文件[图片或其他]路径生成混合的邮件文本
	 * 
	 * @param body
	 *            邮件正文内容
	 * @param filePath
	 *            图片或其他文件路径
	 * @return
	 * @throws Exception
	 */
	public static MimeBodyPart createMailBodyContent(String body, String filePath) throws Exception {
		// 内容
		MimeBodyPart mailBody = new MimeBodyPart();

		// 邮件正文内容[html + image]
		MimeMultipart mailBodyMP = new MimeMultipart("related");
		MimeBodyPart otherPart = new MimeBodyPart();
		MimeBodyPart htmlPart = new MimeBodyPart();
		mailBodyMP.addBodyPart(otherPart);
		mailBodyMP.addBodyPart(htmlPart);

		// 其他内容
		DataSource dataSource = new FileDataSource(new File(filePath));
		DataHandler dataHandler = new DataHandler(dataSource);
		otherPart.setDataHandler(dataHandler);
		otherPart.setContentID(MimeUtility.encodeText(dataSource.getName()));

		// 邮件内容
		MimeMultipart htmlMP = new MimeMultipart("alternative");
		MimeBodyPart htmlContent = new MimeBodyPart();
		htmlContent.setContent(
				"<span style='color: red'>带附件的Html邮件 <img src='cid:" + otherPart.getContentID() + "' /></span>",
				"text/html;charset=UTF-8");
		htmlMP.addBodyPart(htmlContent);
		htmlPart.setContent(htmlMP);

		mailBody.setContent(mailBodyMP);
		return mailBody;
	}

	/**
	 * 根据指定的MimeBodyPart生成邮件
	 * 
	 * @param bodyParts
	 * @return
	 * @throws Exception
	 */
	public static MimeMultipart createMixedBodyContent(MimeBodyPart... bodyParts) throws Exception {
		MimeMultipart mailContent = new MimeMultipart("mixed");

		for (MimeBodyPart mimeBodyPart : bodyParts) {
			mailContent.addBodyPart(mimeBodyPart);
		}

		return mailContent;
	}

	/**
	 * 根据邮件内容生成eml文件
	 * 
	 * @param message
	 * @return
	 * @throws Exception
	 */
	public static File createEmlFile(String dir, Message message) throws Exception {
		File file = new File(dir + MimeUtility.decodeText(message.getSubject()) + ".eml");
		message.writeTo(new FileOutputStream(file));
		return file;
	}

	/**
	 * 发送已生成的eml文件
	 * 
	 * @param emlFile
	 * @param properties
	 * @param auth
	 * @throws Exception
	 */
	public static void sendMailFromEmlFile(File emlFile, Properties properties, Authenticator auth) throws Exception {
		Session session = Session.getInstance(properties, auth);
		sendMailFromEmlFile(emlFile, session);
	}

	/**
	 * 发送已生成的eml文件
	 * 
	 * @param emlFile
	 * @param session
	 * @throws Exception
	 */
	public static void sendMailFromEmlFile(File emlFile, Session session) throws Exception {
		MimeMessage message = new MimeMessage(session, new FileInputStream(emlFile));
		Transport.send(message);
	}

	/**
	 * 获取邮件的主题
	 * 
	 * @param message
	 * @return
	 * @throws Exception
	 */
	public static String getMailSubject(Message message) throws Exception {
		return MimeUtility.decodeText(message.getSubject());
	}

	/**
	 * 获取邮件的发件人
	 * 
	 * @param message
	 * @return
	 * @throws Exception
	 */
	public static String getMailSender(Message message) throws Exception {
		String emailSender = null;
		Address[] addresses = message.getFrom();
		if (addresses == null || addresses.length < 1) {
			throw new IllegalArgumentException("该邮件没有发件人");
		}

		// 获得发件人
		InternetAddress address = (InternetAddress) addresses[0];
		String senderName = address.getPersonal();
		if (senderName != null) {
			senderName = MimeUtility.decodeText(senderName);
			emailSender = senderName + "<" + address.getAddress() + ">";
		} else {
			senderName = address.getAddress();
		}
		return emailSender;
	}

	/**
	 * 根据收件人类型, 获得邮件的收件人
	 * 
	 * @param message
	 * @param recipientType
	 * @return
	 * @throws Exception
	 */
	public static String getMailRecipients(Message message, Message.RecipientType recipientType) throws Exception {
		StringBuilder builder = new StringBuilder();

		Address[] addresses = null;
		if (recipientType == null) {
			addresses = message.getAllRecipients();
		} else {
			addresses = message.getRecipients(recipientType);
		}

		if (addresses == null || addresses.length < 1) {
			throw new IllegalArgumentException("该邮件没有收件人");
		}

		for (Address address : addresses) {
			InternetAddress iAddress = (InternetAddress) address;
			builder.append(iAddress.toUnicodeString()).append(", ");
		}

		return builder.deleteCharAt(builder.length() - 1).toString();
	}

	/**
	 * 获得邮件发送时间
	 * 
	 * @param message
	 * @return
	 * @throws Exception
	 */
	public static String getMailSendDate(Message message, String pattern) throws Exception {
		String sendDateString = null;

		if (pattern == null || "".equals(pattern.trim())) {
			pattern = "yyyy年MM月dd日 E HH:mm";
		}

		Date sendDate = message.getSentDate();
		sendDateString = new SimpleDateFormat(pattern).format(sendDate);

		return sendDateString;
	}

	/**
	 * 邮件是否包含附件<br/>
	 * 
	 * multipart/* 代表附件<br/>
	 * message/rfc822 代表了内嵌消息
	 * 
	 * @param message
	 * @return
	 * @throws Exception
	 */
	public static boolean containsAttachment(Part part) throws Exception {
		boolean flag = false;
		if (part != null) {
			if (part.isMimeType("multipart/*")) {
				MimeMultipart mp = (MimeMultipart) part.getContent();
				for (int i = 0; i < mp.getCount(); i++) {
					BodyPart bodyPart = mp.getBodyPart(i);
					String disposition = bodyPart.getDisposition();
					if (disposition != null && (Part.ATTACHMENT.equalsIgnoreCase(disposition)
							|| Part.INLINE.equalsIgnoreCase(disposition))) {
						flag = true;
					} else if (bodyPart.isMimeType("multipart/*")) {
						flag = containsAttachment(bodyPart);
					} else {
						String contentType = bodyPart.getContentType();
						if (contentType.indexOf("application") != -1) {
							flag = true;
						}

						if (contentType.indexOf("name") != -1) {
							flag = true;
						}
					}
					if (flag)
						break;
				}
			} else if (part.isMimeType("message/rfc822")) {
				flag = containsAttachment((Part) part.getContent());
			}
		}

		return flag;
	}

	/**
	 * 邮件是否已读
	 * 
	 * @param message
	 * @return
	 * @throws Exception
	 */
	public static boolean isSeen(Message message) throws Exception {
		if (message == null) {
			throw new MessagingException("Message is empty");
		}
		return message.getFlags().contains(Flags.Flag.SEEN);
	}

	/**
	 * 邮件是否含有阅读回执
	 * 
	 * @param message
	 * @return
	 * @throws Exception
	 */
	public static boolean isReplaySign(Message message) throws Exception {
		if (message == null) {
			throw new MessagingException("Message is empty");
		}

		boolean replaySign = false;
		String[] headers = message.getHeader("Disposition-Notification-To");
		if (headers != null && headers.length > 0) {
			replaySign = true;
		}

		return replaySign;
	}

	/**
	 * 获得邮件的优先级
	 * 
	 * @param message
	 * @return
	 * @throws Exception
	 */
	public static String getMailPriority(Message message) throws Exception {
		if (message == null) {
			throw new MessagingException("Message is empty");
		}

		String priority = "普通";

		String[] headers = message.getHeader("X-Priority");
		if (headers != null && headers.length > 0) {
			String mailPriority = headers[0];
			if (mailPriority.indexOf("1") != -1 || mailPriority.indexOf("High") != -1) {
				priority = "紧急";
			} else if (mailPriority.indexOf("5") != -1 || mailPriority.indexOf("Low") != -1) {
				priority = "低";
			} else {
				priority = "普通"; // 3或者Normal;
			}

		}

		return priority;
	}

	/**
	 * 获得邮件文本内容
	 * 
	 * @param part
	 * @param content
	 * @throws Exception
	 */
	public static void getMailTextContent(Part part, StringBuilder content) throws Exception {
		if (part == null) {
			throw new MessagingException("Message content is empty");
		}
		boolean containsTextInAttachment = part.getContentType().indexOf("name") > 0;
		if (part.isMimeType("text/*") && containsTextInAttachment) {
			content.append(part.getContent().toString());
		} else if (part.isMimeType("message/rfc822")) {
			getMailTextContent((Part) part.getContent(), content);
		} else if (part.isMimeType("multipart/*")) {
			Multipart mp = (Multipart) part.getContent();
			for (int i = 0; i < mp.getCount(); i++) {
				BodyPart bodyPart = mp.getBodyPart(i);
				getMailTextContent(bodyPart, content);
			}
		} else if (part.isMimeType("image/*")) {
			// TODO part.getInputStream()获得输入流然后输出到指定的目录
		} else {
			// TODO 其它类型的contentType, 未做处理, 直接输出
			content.append(part.getContent().toString());
		}
	}

	/**
	 * 保存附件
	 * 
	 * @param part
	 * @param destDir
	 * @throws Exception
	 */
	public static void saveAttachment(Part part, String destDir) throws Exception {
		if (part == null) {
			throw new MessagingException("part is empty");
		}

		// 复杂的邮件包含多个邮件体
		if (part.isMimeType("multipart/*")) {
			Multipart mp = (Multipart) part.getContent();
			// 遍历每一个邮件体
			for (int i = 0; i < mp.getCount(); i++) {
				BodyPart bodyPart = mp.getBodyPart(i);
				// bodyPart也可能有多个邮件体组成
				String disposition = bodyPart.getDisposition();
				if (disposition == null && (Part.ATTACHMENT.equalsIgnoreCase(disposition)
						|| Part.INLINE.equalsIgnoreCase(disposition))) {
					InputStream in = bodyPart.getInputStream();
					saveFile(in, destDir, decodeText(bodyPart.getFileName()));
				} else if (bodyPart.isMimeType("multipart/*")) {
					saveAttachment(bodyPart, destDir);
				} else {
					String contentType = bodyPart.getContentType();
					if (contentType.indexOf("name") != -1 || contentType.indexOf("application") != -1) {
						saveFile(bodyPart.getInputStream(), destDir, decodeText(bodyPart.getFileName()));
					}
				}
			}
		} else if (part.isMimeType("message/rfc822")) {
			saveAttachment((Part) part.getContent(), destDir);
		}
	}

	/**
	 * 保存文件到指定目录
	 * 
	 * @param in
	 * @param destDir
	 * @param fileName
	 * @throws Exception
	 */
	public static void saveFile(InputStream in, String destDir, String fileName) throws Exception {

		FileOutputStream out = new FileOutputStream(new File(destDir + fileName));

		byte[] buffer = new byte[1024];
		int length = 0;
		while ((length = in.read(buffer)) != -1) {
			out.write(buffer, 0, length);
		}

		out.close();
		in.close();
	}

	/**
	 * 解码文本
	 * 
	 * @param encodedText
	 * @return
	 * @throws Exception
	 */
	public static String decodeText(String encodedText) throws Exception {
		if (encodedText == null || "".equals(encodedText.trim())) {
			return "";
		} else {
			return MimeUtility.decodeText(encodedText);
		}
	}
}
